﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace block_explorer
{
    class KeyEvent
    {
        public enum EventType
        {
            Pressed,
            Released
        }

        private Keys mKey;
        private EventType mEventType;

        public Keys Key { get { return mKey; } }
        public EventType Type { get { return mEventType; } }

        public KeyEvent(Keys key, EventType eventType)
        {
            mKey = key;
            mEventType = eventType;
        }
    }

    class MouseButtonEvent
    {
        public enum EventType
        {
            Pressed,
            Released
        }

        public enum Buttons
        {
            Left,
            Middle,
            Right
        }

        private Buttons mButton;
        private EventType mEventType;

        public Buttons Button { get { return mButton; } }
        public EventType Type { get { return mEventType; } }

        public MouseButtonEvent(Buttons button, EventType eventType)
        {
            mButton = button;
            mEventType = eventType;
        }
    }

    class InputManager : GameComponent
    {
        private KeyboardState mOldKeyboardState;
        private MouseState mOldMouseState;
        private GamePadState mOldGamepadState;

        private IDictionary<KeyEvent, List<Action>> mKeyActions;
        private IDictionary<MouseButtonEvent, List<Action>> mMouseButtonActions;
        private IDictionary<Buttons, List<Action>> mGamepadActions;

        public KeyboardState KeyboardState { get { return mOldKeyboardState; } }
        public GamePadState GamepadState { get { return mOldGamepadState; } }

        public InputManager(BlockExplorer game)
            :base(game)
        {
            mOldKeyboardState = Keyboard.GetState();
            mOldGamepadState = GamePad.GetState(PlayerIndex.One);
            mKeyActions = new Dictionary<KeyEvent, List<Action>>();
            mMouseButtonActions = new Dictionary<MouseButtonEvent, List<Action>>();
            mGamepadActions = new Dictionary<Buttons, List<Action>>();
        }

        public void RegisterKeyEvent(KeyEvent key, Action action)
        {
            if (!mKeyActions.ContainsKey(key))
                mKeyActions[key] = new List<Action>();
            mKeyActions[key].Add(action);
        }

        public void RegisterMouseButtonEvent(MouseButtonEvent button, Action action)
        {
            if (!mMouseButtonActions.ContainsKey(button))
                mMouseButtonActions[button] = new List<Action>();
            mMouseButtonActions[button].Add(action);
        }

        public void RegisterGamepadEvent(Buttons button, Action action)
        {
            if (!mGamepadActions.ContainsKey(button))
                mGamepadActions[button] = new List<Action>();
            mGamepadActions[button].Add(action);
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            UpdateKeyboard(gameTime);
            UpdateGamepad(gameTime);
            UpdateMouse(gameTime);
        }

        private void UpdateKeyboard(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();

            foreach (var keyEventAndActionList in mKeyActions)
            {
                KeyEvent key = keyEventAndActionList.Key;
                List<Action> actionList = keyEventAndActionList.Value;

                if (key.Type == KeyEvent.EventType.Pressed)
                {
                    if (!mOldKeyboardState.IsKeyDown(key.Key) && keyboardState.IsKeyDown(key.Key))
                    {
                        CallAllActions(actionList);
                    }
                }
                else if (key.Type == KeyEvent.EventType.Released)
                {
                    if (mOldKeyboardState.IsKeyDown(key.Key) && !keyboardState.IsKeyDown(key.Key))
                    {
                        CallAllActions(actionList);
                    }
                }
            }

            mOldKeyboardState = keyboardState;
        }

        private void UpdateGamepad(GameTime gameTime)
        {
            GamePadState gamepadSate = GamePad.GetState(PlayerIndex.One);

            foreach (var buttonAndActionList in mGamepadActions)
            {
                Buttons button = buttonAndActionList.Key;
                List<Action> actionList = buttonAndActionList.Value;

                if (!mOldGamepadState.IsButtonDown(button) && gamepadSate.IsButtonDown(button))
                {
                    CallAllActions(actionList);
                }
            }

            mOldGamepadState = gamepadSate;
        }

        private void UpdateMouse(GameTime gameTime)
        {
            MouseState mouseState = Mouse.GetState();

            foreach (var mouseButtonEventAndActionList in mMouseButtonActions)
            {
                MouseButtonEvent button = mouseButtonEventAndActionList.Key;
                List<Action> actionList = mouseButtonEventAndActionList.Value;

                ButtonState oldState;
                ButtonState newState;
                if (button.Button == MouseButtonEvent.Buttons.Left)
                {
                    oldState = mOldMouseState.LeftButton;
                    newState = mouseState.LeftButton;
                }
                else if (button.Button == MouseButtonEvent.Buttons.Middle)
                {
                    oldState = mOldMouseState.MiddleButton;
                    newState = mouseState.MiddleButton;
                }
                else
                {
                    oldState = mOldMouseState.RightButton;
                    newState = mouseState.RightButton;
                }

                if (button.Type == MouseButtonEvent.EventType.Pressed)
                {
                    if (oldState == ButtonState.Released && newState == ButtonState.Pressed)
                    {
                        CallAllActions(actionList);
                    }
                }
                else if (button.Type == MouseButtonEvent.EventType.Released)
                {
                    if (oldState == ButtonState.Pressed && newState == ButtonState.Released)
                    {
                        CallAllActions(actionList);
                    }
                }
            }

            mOldMouseState = mouseState;
        }

        private void CallAllActions(IList<Action> actions)
        {
            foreach (Action action in actions)
            {
                action();
            }
        }
    }
}
